package myApp.ChatServlet

import javax.servlet.http._
import javax.servlet.AsyncContext
import javax.servlet.ServletConfig
import scala.xml._
import scala.collection.JavaConversions._
import scala.util.matching.Regex

// This is a very simple Scala servlet.
// If you rename this class, you must also change the file
// src/main/webapp/WEB-INF/web.xml to use the new name.
class ChatServlet extends HttpServlet {
  import scala.collection.mutable.ListBuffer
  import scala.collection.mutable.Queue
  import java.util.concurrent.ConcurrentLinkedQueue

  /**
   * A queue of saved connection contexts. Use a concurrent queue since multiple threads will access it.
   * When a new event happens, all contexts in the queue are notified.
   */
  val contexts = new ConcurrentLinkedQueue[AsyncContext]

  /**
   * Saved messages. Each time a message is received here, we send to entire list back to the waiting clients.
   */
  val fullmessages = new ListBuffer[MessageDate]
  
  case class MessageDate(text: String, date: java.util.Date)

  /**
   * Send all the messages in a response.
   * Better would be to send only the messages they haven't seen, but that's harder to implement, both at the client and the server.
   */
  def sendMessages(resp: HttpServletResponse) = {
    val div = <ul>
                {
                  for (msg <- fullmessages.reverse) yield <li class="message"><span class="time">[{msg.date.toLocaleString}]</span><span class="text">{ msg.text }</span></li>
                }
              </ul>

    resp.setContentType("text/html")
    resp.setStatus(HttpServletResponse.SC_OK)
    writeXML(resp, div)
  }

  /** Update all the waiting clients. This should clear the contexts queue. */
  def update = {
    println("   updating")

    while (!contexts.isEmpty) {
      contexts.poll match {
        case null =>
        case c =>
          println("   update " + c)
          c.getResponse match {
            case resp: HttpServletResponse => sendMessages(resp)
            case _ => // not an HTTP servlet -- should not happen
              throw new MatchError
          }
          c.complete // close the connection
      }
    }
  }

  /** Write XML back in a response. */
  def writeXML(resp: HttpServletResponse, ns: NodeSeq) = resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(ns))

  
  
  
  
  override def service(req: HttpServletRequest, resp: HttpServletResponse) {
    
    println(req.getRequestURI + " " + req.getParameterMap.toMap)
    println(" + current messages = " + fullmessages)
    println(" + current contexts = " + contexts)
    req.getRequestURI match {
      case "/" =>
        resp.setContentType("text/html")
        resp.getWriter.write("<!DOCTYPE html>")
        writeXML(resp, chatPage)
      case "/update" =>
        val msg = req.getParameter("message")
        println("   message = " + msg)
        // Record the message and update anyone who's waiting on us
        fullmessages += MessageDate(msg, new java.util.Date)
        update
        resp.setStatus(200)
        resp.setContentLength(0)
        resp.setContentType("application/xml")
      case "/socket" =>
        // Save the connection context in a queue.
        val c = req.startAsync
        c.setTimeout(0) // keep collection alive forever
        contexts offer c
      case "/poll" =>
        sendMessages(resp)
    }
    println(" - current messages = " + fullmessages)
    println(" - current contexts = " + contexts)
  }

  
  
  
  
  def chatPage: NodeSeq = {
    val js = """
      function updateMessages(xml) {
        $("div#messages").html(xml);
      }
      
      function poll() {
        $.get("/poll", function (x) { updateMessages(x); });
      }

      // Long poll with a 30 second timeout.
      function longPoll() {
        $.ajax({ url: "/socket", success: function (x) { updateMessages(x); }, dataType: "html", complete: longPoll, timeout: 30000 });
      }

      $(document).ready(function() {
        $("form#chat").submit(function () {
          var msg = $("input#message").val();
          $("label#sent").html("Sent " + msg);
          $("input#message").val("");
          $.get("/update", { message: msg });
          return false;
        });
      });
      
      $(document).ready(function() {
          // setInterval(poll, 2000);
          poll(); // get the initial messages
          longPoll(); // wait for more
      });
   """

      val css = """
#messages ul {
          list-style-type: none;
        margin-bottom: 1em;
}
#messages ul li {
        width: 100%;
        padding-bottom: .25em;
}
        #messages ul li.message span.time {
        font-size: smaller;
        color: #ccc;
  }
"""
        
    <html>
      <head>
        <title>Chat</title>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js" type="text/javascript"/>
        <script type="text/javascript">{ new Unparsed(js) /* use Unparsed to avoid escaping " to &quot; */ }</script>
    <style type="text/css">{ new Unparsed(css) }</style>
      </head>
      <body>
        <h3>Chat</h3>
        <form id="chat" action="">
          <fieldset>
            <label for="message">Enter your message:</label>
    		<input type="text" name="subject" id="subject"/>
            <input type="text" name="message" id="message"/>
            <label for="message" id="sent"/>
            <br/>
            <input type="submit" name="submit" id="submit" value="Send"/>
          </fieldset>
        </form>
        <div id="messages"/>
      </body>
    </html>
  }

}